#ifndef __M_SINK_H__
#define __M_SINK_H__
#include <fstream>
#include "util.hpp"
#include "format.hpp"

class LogSink
{
public:
    using ptr = std::shared_ptr<LogSink>;
    LogSink() {}
    virtual ~LogSink(){}
    virtual void log(const char* data, size_t len) = 0;
};

// 标准输出
class StdoutSink :public LogSink
{
public:
    // 日志写入到标准输出
    void log(const char *data, size_t len)
    {
        // 不能用cout直接输出，不确定是不是0结尾的字符串
        std::cout.write(data, len);
    }
};

// 指定文件
class FileSink :public LogSink
{
public:
    FileSink(const std::string& pathname)
            :_pathname(pathname)
    {
        // 构造时创建日志文件目录，打开
        File::create_directory(File::path(_pathname));
        // 追加写二进制
        _ofs.open(_pathname, std::ios::binary | std::ios::app);
        assert(_ofs.is_open());
    }

    void log(const char *data, size_t len)
    {
        _ofs.write(data, len);
        // 写入失败退出
        assert(_ofs.good());
    }

private:
    std::string _pathname;
    std::ofstream _ofs;
};

// 滚动文件
class RollBySizeSink :public LogSink
{
public:
    RollBySizeSink(const std::string& basename, size_t max_fsize)
                :_basename(basename), _max_fsize(max_fsize), _cur_fsize(0), _name_count(0)
    {
        // 获取创建的文件名
        std::string pathname = createNewFile();
        // 构造时创建日志文件目录，打开
        File::create_directory(File::path(pathname));
        // 追加写二进制
        _ofs.open(pathname, std::ios::binary | std::ios::app);
        assert(_ofs.is_open());
    }   
    void log(const char *data, size_t len)
    {
        // 判断当前文件大小
        if (_cur_fsize > _max_fsize)
        {
            std::string pathname = createNewFile();
            // 一定要关闭旧的文件
            _ofs.close();
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
            _cur_fsize = 0;
        }

        _ofs.write(data, len);
        assert(_ofs.good());
        _cur_fsize += len;
    }

private:
    std::string createNewFile()  // 超过大小创建新文件，获取文件名
    {
        time_t time = Date::now();
        struct tm lt;
        localtime_r(&time, &lt);
        // 追加文件名
        std::stringstream filename;
        filename << _basename;
        filename << lt.tm_year + 1900;  // 从1900年开始的
        filename << lt.tm_mon + 1;  // 0月开始算的
        filename << lt.tm_mday;
        filename << lt.tm_hour;
        filename << lt.tm_min;
        filename << lt.tm_sec;
        filename << "-";
        filename << _name_count++;
        filename << ".log";

        return filename.str();
    }

private:
    std::string _basename;  // 基准文件名，根据时间追加文件名 ./log/base-2022034233
    std::ofstream _ofs;
    size_t _max_fsize;  // 最大大小，文件超过切换文件
    size_t _cur_fsize;  // 当前文件已经写入数据的大小
    size_t _name_count;  // 文件名补充，防止写入过快文件名相同
};

// 落地工厂，使用工厂模式创建落地方式对象
class SinkFactory
{
public:
    // 每个落地参数个数不同，使用可变参数列表
    template <typename SinkType, typename ...Args>
    static LogSink::ptr create(Args... args)
    {
        // 完美转发
        return std::make_shared<SinkType>(std::forward<Args>(args)...);
    }
};
#endif